#include "tcpserver.h"

TcpServer::TcpServer(QObject *parent, int port)
{
    // 开始监听来自任意主机的连接，端口号来自构造函数的参数
    listen(QHostAddress::Any, port);
    // 当有新连接时，将该用户添加到用户列表
    connect(this, &TcpServer::signalNewClient, this, &TcpServer::newClientSlot);
    // 当玩家人数符合要求时，允许开始游戏
    connect(this, &TcpServer::signalAllowToPlay, this, &TcpServer::allowToPlaySlot);

    isGamePlaying = false;

    // 玩家允许作答的时长
    totalTime = 90;
    // 初始化计时器
    outerTimer = new QTimer(this);
    outerTimer->setInterval(totalTime * 1000);
    // 倒计时结束时，本回合结束
    connect(outerTimer, &QTimer::timeout, this, &TcpServer::outerTimerSlot);
    // 初始化计时器（用于更新剩余时间）
    innerTimer = new QTimer(this);
    innerTimer->setInterval(1000);
    // 当一秒过去时，更新剩余时间
    connect(innerTimer, &QTimer::timeout, this, &TcpServer::innerTimerSlot);

    // 打开外部的词库文件，获取词库内容
    QFile wordFile(":/words/words.txt");
    // 以只读方式打开文件
    wordFile.open(QFile::ReadOnly);
    // qDebug() << QDir::currentPath();
    // 获取词库内容
    QString words = wordFile.readAll();
    // 对词语进行切分
    wordList = words.split(';');
    wordFile.close();
    // 为了每次运行程序时，都可以产生不同的随机数，我们要使用qsrand()设置一个不同的初值
    // qsrand(QTime::currentTime().msec() + QTime::currentTime().second() * 1000);
}

TcpServer::~TcpServer()
{
    close();
}

void TcpServer::incomingConnection(qintptr descriptor)
{
    TcpSocket *clientSocket = new TcpSocket(this);
    // 当套接字对象收到来自客户端的消息时，执行处理消息的函数
    connect(clientSocket, &TcpSocket::msgReceived, this, &TcpServer::msgReceivedSlot);
    // 当连接断开时，调用服务端的对应方法
    connect(clientSocket, &TcpSocket::clientDisconnected, this, &TcpServer::clientDisconnectedSlot);
    // 设定套接字描述符
    clientSocket->setSocketDescriptor(descriptor);
    addPendingConnection(clientSocket);
    // 将描述符添加到描述符列表中
    clientSocketList.append(clientSocket);
    // 当列表中的客户端大于 1 个时，发送允许服务端开始游戏的信号
    if(clientSocketList.size() > 1 && !isGamePlaying)
        emit signalAllowToPlay(true);
    // 获取客户端的地址
    QHostAddress const clientInfo = clientSocket->peerAddress();
    // 发送用户上线的信号
    emit signalNewClient(clientInfo.toString(), descriptor);
}

void TcpServer::msgReceivedSlot(const QByteArray& msg, int descriptor)
{
    // 存放消息内容
    QList<QByteArray> msgSplitList;
    // 判断消息类型，1 是有用户上线
    if('1' == msg.at(0))
    {
        // 切分消息内容
        msgSplitList = msg.split(';');
        // 添加用户名至用户名列表
        userNameList.append(msgSplitList.at(1));
        // 将用户名列表拼接成一条消息
        QByteArray datagram = "1;";
        // 把用户名列表转换成 QByteArray
        for(const auto & i : userNameList)
        {
            // qDebug() << userNameList.at(i);
            // QString 转换成 QByteArray 类型才能被添加到 QByteArray 中
            datagram.append(i.toUtf8());
            datagram.append(';');
        }
        // 向所有客户端广播用户名列表
        for(auto i : clientSocketList)
            i->write(datagram);
        // qDebug() << datagram;
    }
    // 用户发送消息
    else if('3' == msg.at(0))
    {
        handleResult(msg, descriptor);
    }
    // 用户弃权
    else if('8' == msg.at(0))
    {
        msgSplitList = msg.split(';');
        sendMsg(3, QString(msgSplitList.at(1).data()), QString(tr(" 放弃本轮，即将进行下一轮")));
        stopDrawingSlot();
        takeTurnsToDraw();
    }
    // 处理其它消息
    else
    {
        for(auto i : clientSocketList)
            i->write(msg);
    }
    // 发送信号给图形界面类
    emit singalNewMsg(msg);
}

void TcpServer::clientDisconnectedSlot(int descriptor)
{
    for(int i = 0; i < clientSocketList.size(); i++)
    {
        // 找到描述符之后，将其从套接字列表和用户名列表删除
        if(clientSocketList[i]->socketDescriptor() == descriptor)
        {
            // 发送下线消息
            sendMsg(0, i, QString(userNameList.at(i)));
            clientSocketList.removeAt(i);
            // 发送信号给图形界面类
            emit signalClientDisconnected(i);
            userNameList.removeAt(i);
            // 如果退出的玩家是画家，切换到下一个玩家作为画家
            if(i == currentPainter && clientSocketList.size() > 1)
            {
                // stopDrawingSlot();
                takeTurnsToDraw();
            }
            // 跳出循环
            break;
        }
    }
    // 如果在线用户数量小于 2，置灰开始游戏按钮
    if(clientSocketList.size() < 2)
    {
        emit signalAllowToPlay(false);
        // 如果当前正在游戏中，结束游戏
        if(isGamePlaying)
        {
            isGamePlaying = false;
            sendMsg(3, QString(tr("系统")), tr("玩家数量不足，游戏结束"));
            callDelay(1);
            sendMsg(4);
        }
    }
}

void TcpServer::newClientSlot(const QString& name, int descriptor)
{

}

void TcpServer::allowToPlaySlot(bool flag)
{
    if(!flag)
    {
        // 重置计时器
        outerTimer->stop();
        innerTimer->stop();
        totalTime = 90;
        // 将第一个玩家设为画家
        currentPainter = 0;
        isGamePlaying = false;
    }
}

void TcpServer::startGameSlot()
{
    isGamePlaying = true;
    currentPainter = 0;
    takeTurnsToDraw();
}

void TcpServer::outerTimerSlot()
{
    // 倒计时结束，无人猜中词语
    sendMsg(3, QString(tr("系统")), QString(tr("没有人猜中，正确答案是<font><b>") + getCurrentWord() + "。</b></font>"));
    stopDrawingSlot();
    takeTurnsToDraw();
}

void TcpServer::innerTimerSlot()
{
    // 更新剩余时间
    sendMsg(7, --totalTime);
}

void TcpServer::stopDrawingSlot()
{
    // 停止当前玩家作画
    outerTimer->stop();
    innerTimer->stop();
    totalTime = 90;
    // 切换画家
    sendMsg(6);
    currentPainter++;
    callDelay(1);
}

void TcpServer::callDelay(int time)
{
    // 令程序等待，但是不会冻结 UI
    QTime dieTime = QTime::currentTime().addSecs(time);
    while (QTime::currentTime() < dieTime)
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}

void TcpServer::takeTurnsToDraw()
{
    // 确定画家
    if(isGamePlaying)
    {
        if(userNameList.size() > 1)
        {
            // 如果当前画家的编号超出描述符列表的元素数量
            if(currentPainter >= clientSocketList.size())
                currentPainter = 0;
        }
        // 发送清屏信息
        sendMsg(4);
        // 等待
        callDelay(1);
        sendMsg(3, QString(tr("系统")), QString(userNameList.at(currentPainter) + " 正在作画..."));
        callDelay(1);
        // 确定本回合的词语，利用随机数生成器
        setCurrentWord(wordList.at(QRandomGenerator::global()->bounded(0, wordList.size() - 1)));
        // 下发词语给画家
        sendMsg(5, getCurrentWord());
        // 开启计时器
        outerTimer->start();
        innerTimer->start();
    }
}

void TcpServer::handleResult(const QByteArray& result, int descriptor)
{
    // 切分消息
    QList<QByteArray> msgSplitList = result.split(';');
    // 回答正确
    if(getCurrentWord() == msgSplitList.at(2))
    {
        // 向回答正确的玩家发送消息
        for(int i = 0; i < clientSocketList.size(); ++i)
        {
            if(descriptor == clientSocketList.at(i)->socketDescriptor())
            {
                sendMsg(9, userNameList.at(i), getCurrentWord());
                break;
            }
        }
        // 停止当前玩家作画
        stopDrawingSlot();
        // 切换画家
        takeTurnsToDraw();
    }
    else
    {
        for(auto i : clientSocketList)
        {
            i->write(result);
        }
    }
}

// 处理消息的函数
// 消息类型 6：切换画家
void TcpServer::sendMsg(int type)
{
    if(type != 6)
        return;
    QByteArray datagram;
    // 拼接消息
    datagram.append(QString::number(type).toUtf8());
    datagram.append(';');
    // 将消息发给当前画家
    clientSocketList.at(currentPainter)->write(datagram);
}
// 消息类型 7：剩余时间
void TcpServer::sendMsg(int type, int time)
{
    if(type != 7)
        return;
    QByteArray datagram;
    // 拼接消息内容
    datagram.append(QString::number(type).toUtf8());
    datagram.append(';');
    // qDebug() << QString::number(time);
    datagram.append(QString::number(time).toUtf8());
    datagram.append(';');
    // 广播给所有客户端
    for(auto i : clientSocketList)
    {
        i->write(datagram);
    }
}
// 消息类型 5：发给画家的词语
void TcpServer::sendMsg(int type, const QString& word)
{
    if(type != 5)
        return;
    QByteArray datagram;
    // 拼接消息内容
    datagram.append(QString::number(type).toUtf8());
    datagram.append(';');
    datagram.append(word.toUtf8());
    datagram.append(';');
    // 将消息发送给当前画家
    clientSocketList.at(currentPainter)->write(datagram);
}
// 消息类型 3：用户发言、8：弃权、9：游戏结果
void TcpServer::sendMsg(int type, const QString& name, const QString& word)
{
    if(type != 3 && type != 8 && type != 9)
        return;
    QByteArray datagram;
    // 拼接消息内容
    datagram.append(QString::number(type).toUtf8());
    datagram.append(';');
    datagram.append(name.toUtf8());
    datagram.append(';');
    datagram.append(word.toUtf8());
    datagram.append(';');
    // 广播消息
    for(auto i : clientSocketList)
        i->write(datagram);
}
// 消息类型0：用户下线
void TcpServer::sendMsg(int type, int number, const QString& name)
{
    if(type != 0)
        return;
    QByteArray datagram;
    // 拼接消息内容
    datagram.append(QString::number(type).toUtf8());
    datagram.append(';');
    datagram.append(QString::number(number).toUtf8());
    datagram.append(';');
    datagram.append(name.toUtf8());
    datagram.append(';');
    // 广播消息
    for(auto i : clientSocketList)
        i->write(datagram);
}
